----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    19:57:17 12/01/2008 
-- Design Name: 
-- Module Name:    butterfly - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 1.00a 	- butterfly has correct output, both forward and reverse
--			1.01a	- changed mode to unscaled, as large inputs will result in overflow
-- Additional Comments: 
--	
-- IMPORTANT NOTES:
--	Twiddle factor is assumed to be a 16 bit fixed point
-- signed number (so between 1/16384 = (+/-).0000610 and (+/-)1.000
--
-- the output is a 16+stage_number bit 2's complement integer
-- output is achieved by shifting out the fractional bits
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity butterfly is
    Port ( 	clk : in  STD_LOGIC;
			reset : in  STD_LOGIC;
			start : in std_logic;
			fwd_inv: in std_logic;
			x0_re : in  STD_LOGIC_vector(23 downto 0);
			x0_im : in  STD_LOGIC_vector(23 downto 0);
			x1_re : in  STD_LOGIC_vector(23 downto 0);
			x1_im : in  STD_LOGIC_vector(23 downto 0);
			twiddle_re: in std_logic_vector(15 downto 0);
			twiddle_im: in std_logic_vector(15 downto 0);
			y0_re : out  STD_LOGIC_vector(23 downto 0);
			y0_im : out  STD_LOGIC_vector(23 downto 0);
			y1_re : out  STD_LOGIC_vector(23 downto 0);
			y1_im : out  STD_LOGIC_vector(23 downto 0);
			data_valid: out std_logic
			);
end butterfly;

architecture Behavioral of butterfly is
	signal temp_re 	: std_logic_vector(39 downto 0) := (others => '0');		--room for multiplication result (24+16=40 bits)
	signal temp_im 	: std_logic_vector(39 downto 0) := (others => '0');		--room for multiplication result (24+16=40 bits)
	
	signal temp_re_a 	: std_logic_vector(39 downto 0) := (others => '0');		--room for multiplication result (24+16=40 bits)
	signal temp_im_a 	: std_logic_vector(39 downto 0) := (others => '0');		--room for multiplication result (24+16=40 bits)
	signal temp_re_b 	: std_logic_vector(39 downto 0) := (others => '0');		--room for multiplication result (24+16=40 bits)
	signal temp_im_b 	: std_logic_vector(39 downto 0) := (others => '0');		--room for multiplication result (24+16=40 bits)
	
	signal delay		: std_logic_vector(1 downto 0)	:= "00";
	signal mult_delay	: std_logic := '0';
begin
	
	process(clk, reset)
	begin
		if(reset = '0') then
			y0_re <= (others => '0');
			y0_im <= (others => '0');
			y1_re <= (others => '0');
			y1_im <= (others => '0');
			temp_re_a <= (others => '0');
			temp_re_b <= (others => '0');
			temp_im_a <= (others => '0');
			temp_im_b <= (others => '0');
			temp_re <= (others => '0');
			temp_im <= (others => '0');
			data_valid <= '0';
			delay <= "00";
			mult_delay <= '0';
		elsif(clk'event and clk = '1') then
			if(start = '1') then
				if (delay = "00") then
					if mult_delay = '0' then
						temp_re_a <= twiddle_re * x1_re;							--create separate cycle for mult, in order to shorten critical path
						temp_re_b <= twiddle_im * x1_im;
						temp_im_a <= twiddle_im * x1_re;
						temp_im_b <= twiddle_re * x1_im;
						mult_delay <= '1';
						data_valid <= '0';
						delay <= "00";
					else
						if fwd_inv = '1' then
							temp_re <= shr(temp_re_a - temp_re_b, "1110");			--shift right by 14
							temp_im <= shr(temp_im_a + temp_im_b, "1110");			--shift right by 14
						else
							temp_re <= shr(temp_re_a + temp_re_b, "1110");			--shift right by 14
							temp_im <= shr(-temp_im_a + temp_im_b, "1110");			--inverse transform has a negative imaginary part
						end if;
						mult_delay <= '0';
						data_valid <= '0';
						delay <= "01";
					end if;
				elsif delay = "01" then
					if fwd_inv = '1' then
						y1_re <= x0_re - temp_re(23 downto 0);
						y1_im <= x0_im - temp_im(23 downto 0);
						y0_re <= x0_re + temp_re(23 downto 0);
						y0_im <= x0_im + temp_im(23 downto 0);
					else
						y1_re <= shr(x0_re - temp_re(23 downto 0), "0001");
						y1_im <= shr(x0_im - temp_im(23 downto 0), "0001");
						y0_re <= shr(x0_re + temp_re(23 downto 0), "0001");
						y0_im <= shr(x0_im + temp_im(23 downto 0), "0001");
					end if;
					delay <= "10";
					data_valid <= '1';
				else																--wait for output to be registered, and start to go low
					data_valid <= '0';
					delay <= "00";
				end if;
			else
				temp_re <= (others => '0');
				temp_im <= (others => '0');
				data_valid <= '0';
				delay <= "00";
			end if;
		end if;
	end process;
end Behavioral;
